package com.xi.jetcomposedemo.activity

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.calculateTargetValue
import androidx.compose.animation.splineBasedDecay
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.horizontalDrag
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.input.pointer.consumePositionChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.xi.jetcomposedemo.ui.compose.AppSkeleton
import com.xi.jetcomposedemo.ui.theme.BlueGray400
import com.xi.jetcomposedemo.ui.theme.Gray500
import com.xi.jetcomposedemo.ui.theme.Yellow200
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.math.absoluteValue
import kotlin.math.roundToInt

/**
 * 手势动画
 */
class TestGestureAnimationActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AppSkeleton {
                Surface(modifier = Modifier.fillMaxSize().background(Gray500)) {
                    content()
                }
            }
        }
    }

    @Composable
    private fun content() {
        var messages = remember {
            mutableStateListOf(
                "学而时习之，不亦说乎？",
                "有朋自远方来，不亦乐乎？",
                "不知而不愠，不亦君子乎？",
                "慎终，追远，民德归厚矣！",
                "温故而知新，可以为师矣。",
                "学而不思则罔，思而不学则殆。",
                "君使臣以礼，臣事君以忠。",
                "德不孤，必有邻。",
                "敏而好学，不耻下问，是以谓之‘文’也。",
                "知之者不如好之者，好之者不如乐之者。"
            )
        }
        val scrollState = rememberLazyListState()
        LazyColumn(
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentHeight()
                .padding(horizontal = 15.dp)
                .background(BlueGray400),
            state = scrollState
        ) {
            items(messages) {
                Surface(
                    modifier = Modifier
                        .fillMaxWidth()
                        .swipeToDismiss {
                            messages.remove(it)
                        },
                    shadowElevation = 2.dp,
                ) {
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .background(Yellow200)
                            .padding(16.dp),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Icon(imageVector = Icons.Default.Done, contentDescription = null)
                        Spacer(modifier = Modifier.width(6.dp))
                        Text(
                            text = it,
                        )
                    }
                }

            }
        }
    }

    @Composable
    private fun Modifier.swipeToDismiss(
        onDismissed: () -> Unit
    ): Modifier  = composed {
        val offsetX = remember { Animatable(0f) }

        // 创建一个修饰符，用于处理修改元素区域内的光标输入。
        // pointerInput可以调用PointerInputScope.awaitPointerEventScope，
        // 以安装可以等待PointerEventScope的光标输入处理程序。
        pointerInput(Unit) {
            // 衰减动画通常在投掷手势后使用，用于计算投掷动画最后的固定位置
            val decay = splineBasedDecay<Float>(this)
            coroutineScope {
                while (isActive) {
                    // 等待触摸按下事件
                    // awaitPointerEventScope: 挂起并安装指针输入块，该块可以等待输入事件并立即响应它们。
                    val pointerId = awaitPointerEventScope { awaitFirstDown().id }
                    val velocityTracker = VelocityTracker()
                    // 等待拖动事件
                    awaitPointerEventScope {
                        // 监听水平拖动
                        horizontalDrag(pointerId) { change ->
                            val horizontalDragOffset = offsetX.value +  change.positionChange().x
                            // 启动协程执行动画
                            launch {
                                offsetX.snapTo(horizontalDragOffset)
                            }
                            // 记录滑动的位置
                            velocityTracker.addPosition(change.uptimeMillis, change.position)

                            // 消费掉手势事件，而不是传递给外部
                            change.consumePositionChange()
                        }
                    }
                    // 拖动完成，计算投掷的速度。
                    val velocity = velocityTracker.calculateVelocity().x
                    // 我们需要计算投掷的最终位置，以决定是将元素滑回原来的位置，还是将其滑出去并调用回调
                    val targetOffsetX = decay.calculateTargetValue(offsetX.value, velocity)
                    offsetX.updateBounds(
                        lowerBound = -size.width.toFloat(),
                        upperBound = size.width.toFloat()
                    )
                    launch {
                        if (targetOffsetX.absoluteValue <= size.width) {
                            // 滑回原来的位置
                            offsetX.animateTo(targetValue = 0f, initialVelocity = velocity)
                        } else {
                            // 启动衰减动画
                            offsetX.animateDecay(velocity, decay)
                            onDismissed()
                        }
                    }
                }
            }
        }.offset {
            IntOffset(offsetX.value.roundToInt(), 0)
        }
    }

    @Preview(showSystemUi = true, showBackground = true)
    @Composable
    fun previewContent() {
        content()
    }
}